package cn.gailvlun.gll.widget

import android.media.*
import android.os.Build
import android.util.Log
import cn.gailvlun.gll.chatkit.record.IRecord
import cn.gailvlun.gll.chatkit.record.OnSecondChangeListener
import cn.gailvlun.gll.presentation.hotel.event.PermissionEvent
import com.blankj.utilcode.util.FileUtils
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import java.io.BufferedOutputStream
import java.io.DataOutputStream
import java.io.File
import java.io.FileOutputStream
import java.nio.ByteBuffer


class AudioRecordDemo : IRecord {
    override fun startRecord(filePath: String?) {
        this.mFile = FileUtils.getFileByPath(filePath)
        start(true)
    }

    override fun pauseRecord() {
        isRecording = false
    }

    override fun resumeRecord() {
        start(false)
    }

    override fun stopRecord() {
        isRecording = false
    }

    override fun cancelRecord(filePath: String?) {
        stopRecord()
        FileUtils.deleteFile(mFile)
    }

    override fun resetRecord() {
        stopRecord()
        FileUtils.deleteFile(mFile)
        start(true)
    }

    override fun getVolumeRate(): Int {
        
        return 1
    }

    override fun setOnSecondChangeListener(listener: OnSecondChangeListener?) {

    }

    
    private val sampleRateInHz = 44_100
    
    private val channelConfig = AudioFormat.CHANNEL_IN_MONO
    
    private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
    private val minBufferSize: Int
    private val actualBufferSize: Int
    private val mAudioRecord: AudioRecord
    private val BIT_RATE = 64 * 1024
    private var mediaFormat: MediaFormat
    private val mediaCodec: MediaCodec
    private var inputBuffers: Array<ByteBuffer>? = null
    private var outputBuffers: Array<ByteBuffer>? = null

    private var mFile: File? = null

    private val MIME_TYPE = "audio/mp4a-latm"

    init {
        
        minBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)
        actualBufferSize = minBufferSize * 2

        mAudioRecord = AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRateInHz, channelConfig, audioFormat, actualBufferSize)

        
        mediaCodec = MediaCodec.createEncoderByType(MIME_TYPE)
        mediaFormat = MediaFormat.createAudioFormat(MIME_TYPE, sampleRateInHz, 1)
        mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC)
        mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_IN_MONO)
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE)
        mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1)
    }

    var isRecording: Boolean = false
    var outputContainerByteArray: ByteArray? = null

    private fun start(isStart: Boolean) {
        Schedulers.io().createWorker().schedule {
            var dos = DataOutputStream(BufferedOutputStream(FileOutputStream(mFile, !isStart)))
            Log.d("test", "file: " + mFile)

            mAudioRecord.startRecording()
            mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
            mediaCodec.start()

            inputBuffers = mediaCodec.inputBuffers
            outputBuffers = mediaCodec.outputBuffers

            isRecording = true
            while (isRecording) {
                val availableInputIndex = mediaCodec.dequeueInputBuffer(1000)
                if (availableInputIndex > -1) {
                    Log.d("test", "availableInputIndex: $availableInputIndex")
                    val availableBuffer = inputBuffers?.get(availableInputIndex)
                    availableBuffer?.clear()
                    val usefulLen = mAudioRecord.read(availableBuffer, actualBufferSize)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.BRAND.equals("vivo")) {
                        try {
                            mediaCodec.queueInputBuffer(availableInputIndex, 0, usefulLen, System.nanoTime() / 1000, 0)

                        } catch (e: MediaCodec.CodecException){
                                EventBus.getDefault().post(PermissionEvent(false))

                        }
                    }else{
                        mediaCodec.queueInputBuffer(availableInputIndex, 0, usefulLen, System.nanoTime() / 1000, 0)
                    }
                }

                val info = MediaCodec.BufferInfo()
                var availableOutputIndex = mediaCodec.dequeueOutputBuffer(info, 1000)
                while (availableOutputIndex > -1) {
                    Log.d("test", "availableOutputIndex: $availableOutputIndex")
                    val availableBuffer = outputBuffers?.get(availableOutputIndex)

                    val outPacketSize = info.size + 7

                    availableBuffer?.position(info.offset)
                    availableBuffer?.limit(info.offset + info.size)

                    outputContainerByteArray = ByteArray(outPacketSize)

                    addADTStoPacket(outputContainerByteArray, outPacketSize)

                    availableBuffer?.get(outputContainerByteArray, 7, info.size)
                    dos.write(outputContainerByteArray)
                    mediaCodec.releaseOutputBuffer(availableOutputIndex, false)
                    availableOutputIndex = mediaCodec.dequeueOutputBuffer(info, 1000)
                }
            }
            mAudioRecord.stop()
            mediaCodec.stop()
            dos.close()

            inputBuffers = null
            outputBuffers = null
        }
    }

    
    private fun addADTStoPacket(packet: ByteArray?, packetLen: Int) {
        val profile = 2 
        val freqIdx = 4 
        val chanCfg = 1 
        

        packet?.set(0, 0xFF.toByte())
        packet?.set(1, 0xF9.toByte())
        packet?.set(2, ((profile - 1 shl 6) + (freqIdx shl 2) + (chanCfg shr 2)).toByte())
        packet?.set(3, ((chanCfg and 3 shl 6) + (packetLen shr 11)).toByte())
        packet?.set(4, (packetLen and 0x7FF shr 3).toByte())
        packet?.set(5, ((packetLen and 7 shl 5) + 0x1F).toByte())
        packet?.set(6, 0xFC.toByte())
    }

    fun getCurrentFile(): File? {
        return mFile
    }
}